package com.runfast;

import com.runfast.common.web.StringToBaseEnumConverterFactory;
import com.runfast.common.web.interceptor.MaliciousRequestInterceptor;
import com.runfast.common.web.interceptor.UrlPermissionInterceptor;
import com.runfast.common.web.shiro.*;
import com.runfast.paotui.task.async.MessagePushTask;
import com.runfast.pay.wxpay.WXPay;
import com.runfast.pay.wxpay.WXPayConfig;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.amqp.core.*;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Import;
import org.springframework.core.convert.converter.Converter;
import org.springframework.format.FormatterRegistry;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.servlet.Filter;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.Executor;


@SpringBootApplication
@EnableCaching
@ServletComponentScan
@EnableAsync
@EnableScheduling
@Slf4j
public class RunfastApplication implements WebMvcConfigurer, AsyncConfigurer {

    public static void main(String[] args) {
        SpringApplication.run(RunfastApplication.class, args);
    }



    /*@Bean(name = "waimaiDataSource")
    @Qualifier("waimaiDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.waimai")
    public DataSource primaryDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "paotuiDataSource")
    @Qualifier("paotuiDataSource")
    @Primary
    @ConfigurationProperties(prefix = "spring.datasource.paotui")
    public DataSource secondaryDataSource() {
        return DataSourceBuilder.create().build();
    }*/

    @Bean
    public UserRealm userRealm() {
        UserRealm userRealm = new UserRealm();
        return userRealm;
    }


    @Bean
    public DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(userRealm());
        return securityManager;
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        LinkedHashMap<String, Filter> stringFilterMap = new LinkedHashMap<>();
        stringFilterMap.put("jsonAuthc", new JsonAuthenticationFilter());

        JwtPasswordAuthFilter jwtPasswordAuthFilter = new JwtPasswordAuthFilter();
        jwtPasswordAuthFilter.setSuccessUrl("/api/user/account/loginByPwdSuccess");
        stringFilterMap.put("jwtPwdAuthc", jwtPasswordAuthFilter);

        JwtSmsCodeAuthFilter jwtSmsCodeAuthFilter = new JwtSmsCodeAuthFilter();
        jwtSmsCodeAuthFilter.setSuccessUrl("/api/user/account/loginBySmsSuccess");
        stringFilterMap.put("jwtSmsAuthc", jwtSmsCodeAuthFilter);

//        stringFilterMap.put("jsonPerms", new JsonPermissionsAuthorizationFilter());

        shiroFilterFactoryBean.setFilters(stringFilterMap);
        //拦截器.
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        // 配置不会被拦截的链接 顺序判断

        //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
        filterChainDefinitionMap.put("/api/user/account/logout", "logout");
        //<!-- 过滤链定义，从上向下顺序执行，一般将/**放在最为下边 -->:这是一个坑呢，一不小心代码就不好使了;
        //<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->

        filterChainDefinitionMap.put("/api/user/account/loginBySms", "jwtSmsAuthc");
        filterChainDefinitionMap.put("/api/user/home/**", "anon");
        filterChainDefinitionMap.put("/api/**", "jwtPwdAuthc");
        filterChainDefinitionMap.put("/**", "anon");
        // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
        shiroFilterFactoryBean.setLoginUrl("/api/user/account/login");
        // 登录成功后要跳转的链接
        shiroFilterFactoryBean.setSuccessUrl("/api/user/account/success");

        //未授权界面;
        shiroFilterFactoryBean.setUnauthorizedUrl("/api/user/account/unauthorized");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager());
        return advisor;
    }

    @Bean
    @DependsOn("lifecycleBeanPostProcessor")
    public DefaultAdvisorAutoProxyCreator DefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }


    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MaliciousRequestInterceptor()).addPathPatterns("/**");
        registry.addInterceptor(new UrlPermissionInterceptor());
//        registry.addInterceptor(new LogInterceptor());
    }

    /*@Bean
    @Qualifier("jobDetailFactoryBean")
    JobDetailFactoryBean JobDetailFactoryBean() {
        JobDetailFactoryBean jobDetailFactoryBean = new JobDetailFactoryBean();
        jobDetailFactoryBean.setJobClass(CancelOrderJob.class);
        jobDetailFactoryBean.setDurability(true);
        return jobDetailFactoryBean;
    }

    @Bean
    CronTriggerFactoryBean CronTriggerFactoryBean() {
        CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean();
        cronTriggerFactoryBean.setJobDetail(JobDetailFactoryBean().getObject());
        cronTriggerFactoryBean.setCronExpression("0 * * * * ? *");
        return cronTriggerFactoryBean;
    }

    @Bean
    SimpleTriggerFactoryBean SimpleTriggerFactoryBean(@Qualifier("methodInvokingJobDetailFactoryBean") JobDetail jobDetail) {

        SimpleTriggerFactoryBean simpleTriggerFactoryBean = new SimpleTriggerFactoryBean();
        simpleTriggerFactoryBean.setJobDetail(jobDetail);
        simpleTriggerFactoryBean.setStartDelay(1000);
        simpleTriggerFactoryBean.setRepeatInterval(600000);
        return simpleTriggerFactoryBean;

    }

    @Bean
    @Qualifier("methodInvokingJobDetailFactoryBean")
    MethodInvokingJobDetailFactoryBean MethodInvokingJobDetailFactoryBean(ExampleBusinessObject object) {
        MethodInvokingJobDetailFactoryBean methodInvokingJobDetailFactoryBean = new MethodInvokingJobDetailFactoryBean();
        methodInvokingJobDetailFactoryBean.setTargetObject(object);
        methodInvokingJobDetailFactoryBean.setTargetMethod("doIt");
        methodInvokingJobDetailFactoryBean.setConcurrent(false);
        return methodInvokingJobDetailFactoryBean;
    }*/

    /*@Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/doc/index.html");
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
    }*/


    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
        return restTemplateBuilder.build();
    }

    @Bean
    public WXPay wxAppPay(@Qualifier("wxpayAppConfigImpl") WXPayConfig wxPayConfig) throws Exception {

        return new WXPay(wxPayConfig);
    }

    @Bean
    public WXPay wxPublicPay(@Qualifier("wxpayPublicConfigImpl") WXPayConfig wxPayConfig) throws Exception {

        return new WXPay(wxPayConfig);
    }


    @Override
    @Bean
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(20);
        return executor;
    }

    @Override
    @Bean
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new AsyncUncaughtExceptionHandler() {
            @Override
            public void handleUncaughtException(Throwable ex, Method method, Object... params) {
                Class<?> declaringClass = method.getDeclaringClass();

<<<<<<< .mine

                    log.error(String.format("异步消息推送错误，方法： " + "%s 参数：", method, ArrayUtils.toString(params)), ex);
                }
||||||| .r15
                    log.error(String.format("异步消息推送错误，方法： " + "%s 参数：", method, ArrayUtils.toString(params)), ex);
                }
=======
                    log.error(String.format("异步方法异常： 方法 %s 参数 %s", declaringClass.getName()+"."+method, ArrayUtils.toString(params)), ex);
>>>>>>> .r34
            }
        };
    }

    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverterFactory(new StringToBaseEnumConverterFactory());
    }

    @Bean
    public Queue orderManagerQueue(){
        return new Queue("user_order_manager");
    }

    @Bean
    public Queue orderBusinessQueue(){
        return new Queue("user_order_business");
    }

    @Bean
    public Queue orderDriverQueue(){
        return new Queue("user_order_driver");
    }

    @Bean
    public Exchange orderExchage(){

        return new FanoutExchange("user_order_exchange");
    }

    @Bean
    public Binding orderBindingManager(){
        return BindingBuilder.bind(orderManagerQueue()).to(orderExchage()).with("user_order_manager_key").noargs();
    }
    @Bean
    public Binding orderBindingBusiness(){
        return BindingBuilder.bind(orderBusinessQueue()).to(orderExchage()).with("user_order_business_key").noargs();
    }
    @Bean
    public Binding orderBindingDriver(){
        return BindingBuilder.bind(orderDriverQueue()).to(orderExchage()).with("user_order_driver_key").noargs();
    }
}
